package ru.teleology.cubefs.tests;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;

import org.junit.Test;

import ru.teleology.cubefs.api.models.IdxMap;
import ru.teleology.cubefs.api.models.TreeMap;
import ru.teleology.cubefs.api.models.TreeMapIterator;
import ru.teleology.cubefs.api.models.Map.Pair;
import ru.teleology.cubefs.api.reflection.Finalizer;
import ru.teleology.cubefs.api.reflection.Typed;
import ru.teleology.cubefs.api.reflection.list.StaticListOfTyped;
import ru.teleology.cubefs.api.reflection.number.Number;

public class IdxMapTest extends ContainerTestBase {
	
	private static final int SIZE = 1000;
	
	private long[]                  pairs 		= new long[SIZE];
	private List<Long> 		pairs_sorted 	= new ArrayList<Long>();
	private Random 			RND;
	private Set<Long> 		randoms 	= new HashSet<Long>();
	
	
	Comparator<Long> pair_comparator = new Comparator<Long>(){
		public int compare(Long o1, Long o2) {
			long result = o1 - o2;
			return result < 0 ? -1 : result > 0 ? 1 : 0;
		}
	};
	
	//1262265438702
	
	public IdxMapTest() {
		setChildrenPerNode(5);
		long seed = System.currentTimeMillis();
		System.err.println("Seed: "+seed);
		RND = new Random(seed);
		
		for (int c = 0; c < pairs.length; c++) {
			long key 	= (getLongRandom() & 0x7fffffffffffffffl);
			pairs[c] 	= key;
		}
		
		setDump(true);
	}
	
	long getLongRandom() {
		long rnd = 0;
		int cnt = 0;
		while (randoms.contains(rnd = RND.nextInt(10000))) {
			cnt++;
			assertTrue(cnt < 10000);
		}
		randoms.add(rnd);
		return rnd;
	}
	
	@Test 
	public void testInsertRemove() {
		
		final IdxMap map 	= (IdxMap) container.CreateCollection(3, 1);
		map.SetMaxChildrenPerNode(getChildrenPerNode());
		
		final IdxMap.Pair 	pair 	=  map.CreatePairHolder();
//		Finalizer<Typed> 	pairf 	= new Finalizer<Typed>(pair);
//		Finalizer<Typed> 	containerf 	= new Finalizer<Typed>(container);
		
		final StaticListOfTyped keys 		= (StaticListOfTyped) pair.key();
		final Number key 			= (Number) keys.GetElement(0);
		
		run(new Ops("IdxMap_CreateRemove") {{
			for (int c = 0; c < SIZE; c++) {
				final int cc = c;				
				run(new Op(cc+".CreateOp"){{
					key.SetValue(pairs[cc]);
					
					map.Put(pair);
					
					assertEquals(cc + 1, map.GetSize());
					assertEquals(false, container.Check());
					
					pairs_sorted.add(pairs[cc]);
					sort(pairs_sorted);
					
					checkIterator(map, pair, pairs_sorted);
				}}); 
			}
			
			for (int c = 0; c < SIZE; c++) {
				final int cc = c;
				run(new Op(cc+".RemoveOp"){{

					key.SetValue(pairs[cc]);
					map.Remove(pair);
					
					long size = SIZE - cc - 1;
					assertEquals(size, map.GetSize());
					assertEquals(false, container.Check());
					
					pairs_sorted.remove(pairs[cc]);
					
					checkIterator(map, pair, pairs_sorted);
				}});
			}
			
			assertEquals(0, container.GetManager().GetPageCount());
			assertEquals(false, container.Check());
		}});
	}
	
	@Test 
	public void testRemoveBatch() {
		
		for (int d = 0; d < SIZE; d++) {
			pairs_sorted.add(pairs[d]);
		}
		
		sort(pairs_sorted);
		
		run(new Ops("IdxMap_RemoveBatch") {{
			for (int c = 0; c < 4000; c++) {
				createContainer();
				final IdxMap map 	= (IdxMap) container.CreateCollection(3, 1);
				map.SetMaxChildrenPerNode(getChildrenPerNode());
				
				final IdxMap.Pair 	pair 	= map.CreatePairHolder();
//				Finalizer<Typed> 	pairf 	= new Finalizer<Typed>(pair);
				
				final StaticListOfTyped keys 	= (StaticListOfTyped) pair.key();
				final Number key 				= (Number) keys.GetElement(0);
				
				for (int d = 0; d < SIZE; d++) {
					key.SetValue(pairs[d]);
					map.Put(pair);
				}
				
				assertEquals(SIZE, map.GetSize());
				
//				if (c == 95) container.GetManager().GetLogger().SetLogLevel(LogHandler.TRACE);
				final int cc = c;
				run(new Op(cc+".RemoveOp"){{

					int from, to;
					if (cc == 0) {
						from = 0;
						to = SIZE - 1;
					}
					else if (cc == 1) {
						from = 0;
						to = SIZE/2;
					}
					else if (cc == 2) {
						from = SIZE/2;
						to = SIZE - 1;
					}
					else {
						from 	= RND.nextInt(SIZE/2 - 1);
						to 		= SIZE/2 + RND.nextInt(SIZE/2);
					}
					
					List<Long>	result = new ArrayList<Long>();
					for (int d = 0; d < pairs_sorted.size(); d++) {
						if (d < from || d > to) result.add(pairs_sorted.get(d));
					}
					
					IdxMap.Pair from_pair = map.CreatePairHolder();
					final StaticListOfTyped from_keys 		= (StaticListOfTyped) from_pair.key();
					final Number 			 from_key 		= (Number) from_keys.GetElement(0);
					
					IdxMap.Pair to_pair = map.CreatePairHolder();
					final StaticListOfTyped to_keys 		= (StaticListOfTyped) to_pair.key();
					final Number 			 to_key 		= (Number) to_keys.get(0);
					
					from_key.SetValue(pairs_sorted.get(from));
					to_key.SetValue(pairs_sorted.get(to) + 1);
					
					map.Remove(from_pair, to_pair);

					from_pair.destroy();
					to_pair.destroy();
					
					long size = SIZE - (to - from + 1);
					assertEquals(size, map.GetSize());
					assertEquals(false, container.Check());

					checkIterator(map, pair, result);

					assertEquals(false, container.Check());
				}});
				
				pair.destroy();
				container.destroy();
		
//				System.gc();
//				System.gc();
				System.err.println(c + " "+Runtime.getRuntime().freeMemory()/1024/1024);
			}
		}});
		
		System.gc();
		System.gc();
		System.err.println("---------------------------");
		System.err.println(Runtime.getRuntime().freeMemory()/1024/1024);
	}
	
	
	private void sort(List<Long> list) {
		Collections.sort(list, pair_comparator);
	}
	
	private void checkIterator(TreeMap map, Pair pair, List<Long> pairs) {
		TreeMapIterator iter = (TreeMapIterator) map.CreateIterator();
		final StaticListOfTyped keys 	= (StaticListOfTyped) pair.key();
		final Number 			 key 	= (Number) keys.get(0);
		
		int idx = 0;
		while (!iter.IsEof()) {
			iter.GetValue(pair);
			assertEquals(pairs.get(idx).longValue(), key.GetBigIntValue());
			
			iter.Next();
			idx++;
		}
		
		assertEquals(pairs.size(), idx);
	}
}
